package test

//import aquerius.Experimental._

/**
 * @author: Yardena
 * @date: Dec 14, 2009 5:41:41 PM
 */

object Experimental2 {

  trait Domain[+P<:Pattern] {
    def extract[U>:P<:Pattern](pattern: U):Option[U#Value]
    def domain[U>:P<:Pattern](pattern: U):Domain[U]
  }

  trait Pattern {
    type This = this.type
    type Value
    def unapply(domain:Domain[This]):Option[Value] = domain.extract(this)
    def map(f:()=>Value):Domain[This] = new Mapping(f)
    private class Mapping(f:()=>Value) extends Domain[This] {
      def extract[U>:This<:Pattern](pattern: U) = if (Pattern.this == pattern) Some(f()) else None
      def domain[U>:This<:Pattern](pattern: U) = if (Pattern.this == pattern) this else null
    }
  }

  class Map[V](f:()=>V) {
    def as[P<:Pattern{type Value = V}](p: P) = p.map(f)
  }
  implicit def mapper[V](f:()=>V) = new Map[V](f)

  sealed class NilRecord extends Domain[Pattern] {
    def extract[P>:Pattern<:Pattern](pattern: P) = None
    def domain[P>:Pattern<:Pattern](pattern: P) = null
    def ::[P<:Pattern](d: Domain[P]) = new Record(d,this)
  }
  object NilRecord extends NilRecord

  sealed class Record[P1<:Pattern,P2<:Pattern](d1:Domain[P1], d2:Domain[P2])
          extends Domain[P1 with P2]
  {
    type P = P1 with P2
    def ::[Q<:Pattern](d: Domain[Q]):Domain[P with Q] = new Record[Q,P](d,this)
    def extract[U>:P<:Pattern](pattern: U) = d1.extract(pattern).
                                             orElse(d2.extract(pattern)).
                                             asInstanceOf[Option[U#Value]]
    def domain[U>:P<:Pattern](pattern: U):Domain[U] = {
      var d = d1.domain(pattern)
      if (d == null) d = d2.domain(pattern)
      d.asInstanceOf[Domain[U]]
    }
  }

}